/*
 * Copyright (C) 2025 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

extern crate alloc;

use crate::commands::common_command::*;
use crate::common::types::*;
use crate::common::constants::*;
use crate::common::{AuthIntent, AuthTrustLevel, AuthType, ErrorCode, ReuseMode, CHALLENGE_LEN};
use crate::entry::user_auth_ffi::CommandId;
use crate::entry::user_auth_ffi::*;
use crate::traits::command_manager::{AccessPolicy, CommandHandler};
use crate::traits::config_manager::{ConfigManagerRegistry, PinExpiredInfo};
use crate::traits::user_db_manager::{UserDbManagerRegistry, CredentialFilter, Credential};
use crate::traits::executor_manager::{ExecutorManagerRegistry, ExecutorFilter, ExecutorInfo};
use crate::traits::misc_manager::{MiscManagerRegistry};
use crate::traits::time_keeper::TimeKeeperRegistry; 
use crate::impls::default_config_manager::NO_CHECK_PIN_EXPIRED_PERIOD;
use crate::jobs::check_reuse_auth_result::{AuthResultReuseRegistry};
use crate::utils::auth_token;
use crate::{impl_command_handler, log_e, log_i, Box, Vec};
use core::convert::TryFrom;

// GetAvailableStatus - implementation
pub struct GetAvailableStatusInput {
    pub user_id: i32,
    pub auth_type: AuthType,
    pub auth_trust_level: AuthTrustLevel,
}

impl TryFrom<GetAvailableStatusInputFfi> for GetAvailableStatusInput {
    type Error = ErrorCode;

    fn try_from(value: GetAvailableStatusInputFfi) -> core::result::Result<Self, ErrorCode> {
        Ok(GetAvailableStatusInput {
            user_id: value.user_id,
            auth_type: AuthType::try_from(value.auth_type)?,
            auth_trust_level: AuthTrustLevel::try_from(value.auth_trust_level)?,
        })
    }
}

pub type GetAvailableStatusOutput = GetAvailableStatusOutputFfi;

pub trait GetAvailableStatusCommandExecutor {
    fn execute(
        &self,
        input: GetAvailableStatusInput,
    ) -> Result<GetAvailableStatusOutput, ErrorCode>;
}

pub struct DefaultGetAvailableStatusCommandExecutor;

fn get_cred_max_acl(user_id: i32, auth_type: AuthType) -> Result<AuthCapabilityLevel, ErrorCode> {
     let credential_filter: CredentialFilter = Box::new(move |cred: &Credential| {
            cred.user_id == user_id && cred.auth_type == auth_type
        });
    let cred_infos = UserDbManagerRegistry::get().get_credential_list(credential_filter)?;

    let mut max_cred_acl = AuthCapabilityLevel::Acl0;
    for cred in cred_infos {
        if (max_cred_acl as i32) < (cred.capability_level as i32) {
            max_cred_acl = cred.capability_level;
        }
    }
    Ok(max_cred_acl)
}

fn get_all_in_one_executor_info(auth_type: AuthType) -> Result<(ExecutorSecurityLevel, AuthCapabilityLevel), ErrorCode> {
     let executor_filter: ExecutorFilter = Box::new(move |executor_info: &ExecutorInfo| {
            executor_info.auth_type == auth_type && executor_info.role == ExecutorRole::AllInOne
        });
    let executor_infos: Vec<ExecutorInfo> = ExecutorManagerRegistry::get().get_executor_list(executor_filter);
    if executor_infos.len() == 0 {
        log_e!("executor is unregistered, authType:{}", auth_type as i32);
        return Err(ErrorCode::TypeNotSupport);
    }

    let mut max_esl: ExecutorSecurityLevel = ExecutorSecurityLevel::Esl0;
    let mut max_acl: AuthCapabilityLevel = AuthCapabilityLevel::Acl0;
    for executor_info in executor_infos {
        if (max_esl as i32) < (executor_info.esl as i32) {
            max_esl = executor_info.esl;
        }
        if (max_acl as i32) < (executor_info.max_template_acl as i32) {
            max_acl = executor_info.max_template_acl;
        }
    }
    Ok((max_esl, max_acl))
}

fn check_pin_expired_for_cred(user_id: i32, auth_type: AuthType) -> Result<(), ErrorCode> {

    let expired_info = ConfigManagerRegistry::get().get_pin_expired_info(user_id, auth_type)?;
    if expired_info.expired_period == NO_CHECK_PIN_EXPIRED_PERIOD {
        log_i!("pinExpiredPeriod is not set");
        return Ok(());
    }
    let current_time = TimeKeeperRegistry::get().get_ree_time()?;
    if current_time < expired_info.enrolled_sys_time {
        log_e!("bad time, currentTime:{}, pinErollTime:{}", current_time, expired_info.enrolled_sys_time);
        return Err(ErrorCode::GeneralError);
    }
    if current_time - expired_info.enrolled_sys_time > expired_info.expired_period as u64 {
        log_e!("pin is expired, currentTime:{}, enrollTime:{}, expiredPeriod:{}", current_time,
            expired_info.enrolled_sys_time, expired_info.expired_period);
        return Err(ErrorCode::PinExpired);
    }
    Ok(())
}

fn get_available_status(user_id: i32, auth_type: AuthType, auth_trust_level: AuthTrustLevel) -> Result<(), ErrorCode> {
    let enable_status = ConfigManagerRegistry::get().get_enable_status(auth_type, user_id)?;
    if !enable_status {
        log_e!("authType:{} is not support", auth_type as i32);
        return Err(ErrorCode::TypeNotSupport);
    }
    let (max_esl, max_acl) = get_all_in_one_executor_info(auth_type)?;
    let support_atl = MiscManagerRegistry::get().get_auth_trust_level(max_acl, max_esl);
    if auth_trust_level as i32 > support_atl as i32 {
        log_e!("executor atl not support, support atl:{}, esl:{}, acl:{}, caller atl:{}", support_atl as i32, max_esl as i32,
            max_acl as i32, auth_trust_level as i32);
        return Err(ErrorCode::TrustLevelNotSupport);
    }
    let max_acl: AuthCapabilityLevel = get_cred_max_acl(user_id, auth_type)?;
    let support_atl = MiscManagerRegistry::get().get_auth_trust_level(max_acl, max_esl);
    if auth_trust_level as i32 > support_atl as i32 {
        log_e!("cred atl not support, support atl:{}, esl:{}, acl:{}, caller atl:{}", support_atl as i32, max_esl as i32,
            max_acl as i32, auth_trust_level as i32);
        return Err(ErrorCode::NotEnrolled);
    }
    check_pin_expired_for_cred(user_id, auth_type)
}

impl GetAvailableStatusCommandExecutor for DefaultGetAvailableStatusCommandExecutor {
    fn execute(
        &self,
        _input: GetAvailableStatusInput,
    ) -> Result<GetAvailableStatusOutput, ErrorCode> {
        get_available_status(_input.user_id, _input.auth_type, _input.auth_trust_level)?;
        let out = GetAvailableStatusOutput::default();
        Ok(out)
    }
}

impl_command_handler!(
    GetAvailableStatusCommandHandler,
    GetAvailableStatusInputFfi,
    GetAvailableStatusInput,
    GetAvailableStatusOutputFfi,
    Box<dyn GetAvailableStatusCommandExecutor>
);

// GetValidSolution - implementation
#[derive(Clone)]
pub struct GetValidSolutionInput {
    pub user_id: i32,
    pub auth_types: Vec<AuthType>,
    pub auth_trust_level: AuthTrustLevel,
}

impl TryFrom<Int32ArrayFfi> for Vec<AuthType> {
    type Error = ErrorCode;

    fn try_from(value: Int32ArrayFfi) -> core::result::Result<Self, ErrorCode> {
        let int_vec = vec_from_array_with_len(&value.data, value.len)?;
        let mut auth_types = Vec::new();
        for id in int_vec {
            auth_types.push(AuthType::try_from(id)?);
        }
        Ok(auth_types)
    }
}

impl TryFrom<Int32ArrayFfi> for Vec<i32> {
    type Error = ErrorCode;

    fn try_from(value: Int32ArrayFfi) -> core::result::Result<Self, ErrorCode> {
        Ok(vec_from_array_with_len(&value.data, value.len)?)
    }
}

impl TryFrom<GetValidSolutionInputFfi> for GetValidSolutionInput {
    type Error = ErrorCode;

    fn try_from(value: GetValidSolutionInputFfi) -> core::result::Result<Self, ErrorCode> {
        let auth_type_vec = vec_from_array_with_len(&value.auth_types.data, value.auth_types.len)?;
        let auth_types: Vec<AuthType> = auth_type_vec
            .into_iter()
            .map(|id| AuthType::try_from(id as i32))
            .collect::<Result<Vec<_>, _>>()?;

        Ok(GetValidSolutionInput {
            user_id: value.user_id,
            auth_types,
            auth_trust_level: value.auth_trust_level.try_into()?,
        })
    }
}

#[derive(Clone, Default)]
pub struct GetValidSolutionOutput {
    pub valid_types: Vec<AuthType>,
}

impl TryFrom<GetValidSolutionOutput> for GetValidSolutionOutputFfi {
    type Error = ErrorCode;

    fn try_from(value: GetValidSolutionOutput) -> core::result::Result<Self, ErrorCode> {
        let auth_type_ints: Vec<i32> = value.valid_types.into_iter().map(|t| t as i32).collect();
        let (data, len) = _array_from_vec::<i32, MAX_CREDENTIAL_NUM_FFI>(auth_type_ints)?;

        Ok(GetValidSolutionOutputFfi {
            valid_types: Int32ArrayFfi { data, len },
        })
    }
}

pub trait GetValidSolutionCommandExecutor {
    fn execute(&self, input: GetValidSolutionInput) -> Result<GetValidSolutionOutput, ErrorCode>;
}

pub struct DefaultGetValidSolutionCommandExecutor;

impl GetValidSolutionCommandExecutor for DefaultGetValidSolutionCommandExecutor {
    fn execute(&self, input: GetValidSolutionInput) -> Result<GetValidSolutionOutput, ErrorCode> {
        let result_priority = [
            ErrorCode::GeneralError,
            ErrorCode::TypeNotSupport,
            ErrorCode::TrustLevelNotSupport,
            ErrorCode::NotEnrolled,
            ErrorCode::PinExpired,
        ];
        let mut output = GetValidSolutionOutput::default();
        let mut highest_priority_error  = ErrorCode::GeneralError;
        for auth_type in input.auth_types {
            match get_available_status(input.user_id, auth_type, input.auth_trust_level) {
                Ok(_) => {
                        output.valid_types.push(auth_type);
                    },
                Err(check_ret) => {
                    if let Some(pos) = result_priority.iter().position(|&x| x == highest_priority_error) {
                        if let Some(_value) = result_priority[pos + 1..].iter().find(|&&x| x == check_ret) {
                            highest_priority_error = check_ret;
                        }
                    }
                }
            }
        }
        log_i!("{:?}", output.valid_types);
        if output.valid_types.is_empty() {
            return Err(highest_priority_error);
        }
        Ok(output)
    }
}

impl_command_handler!(
    GetValidSolutionCommandHandler,
    GetValidSolutionInputFfi,
    GetValidSolutionInput,
    GetValidSolutionOutputFfi,
    Box<dyn GetValidSolutionCommandExecutor>
);

// BeginAuthentication - implementation
pub struct BeginAuthenticationInput {
    pub context_id: u64,
    pub param: AuthParam,
}

pub struct BeginAuthenticationOutput {
    pub schedule_infos: Vec<ScheduleInfo>,
}

impl Default for BeginAuthenticationOutput {
    fn default() -> Self {
        BeginAuthenticationOutput {
            schedule_infos: Vec::new(),
        }
    }
}

#[derive(Clone)]
pub struct AuthParamBase {
    pub user_id: i32,
    pub auth_trust_level: AuthTrustLevel,
    pub executor_sensor_hint: u32,
    pub challenge: [u8; CHALLENGE_LEN],
}

impl TryFrom<AuthParamBaseFfi> for AuthParamBase {
    type Error = ErrorCode;

    fn try_from(value: AuthParamBaseFfi) -> core::result::Result<Self, ErrorCode> {
        Ok(AuthParamBase {
            user_id: value.user_id,
            auth_trust_level: value.auth_trust_level.try_into()?,
            executor_sensor_hint: value.executor_sensor_hint,
            challenge: value.challenge,
        })
    }
}

#[derive(Clone)]
pub struct AuthParam {
    pub base_param: AuthParamBase,
    pub auth_type: AuthType,
    pub auth_intent: AuthIntent,
    pub is_os_account_verified: bool,
    pub collector_udid: Udid,
    pub is_expired_return_success: bool,
    pub need_root_secret: bool,
}

impl TryFrom<AuthParamFfi> for AuthParam {
    type Error = ErrorCode;

    fn try_from(value: AuthParamFfi) -> core::result::Result<Self, ErrorCode> {
        Ok(AuthParam {
            base_param: value.base_param.try_into()?,
            auth_type: value.auth_type.try_into()?,
            auth_intent: value.auth_intent.try_into()?,
            is_os_account_verified: value.is_os_account_verified != 0,
            collector_udid: Udid(value.collector_udid),
            is_expired_return_success: false,
            need_root_secret: false,
        })
    }
}

impl TryFrom<BeginAuthenticationInputFfi> for BeginAuthenticationInput {
    type Error = ErrorCode;

    fn try_from(value: BeginAuthenticationInputFfi) -> core::result::Result<Self, ErrorCode> {
        Ok(BeginAuthenticationInput {
            context_id: value.context_id,
            param: value.param.try_into()?,
        })
    }
}

impl TryFrom<BeginAuthenticationOutput> for BeginAuthenticationOutputFfi {
    type Error = ErrorCode;

    fn try_from(value: BeginAuthenticationOutput) -> core::result::Result<Self, ErrorCode> {
        let mut schedule_infos = ScheduleInfoArrayFfi::default();
        schedule_infos.len = value.schedule_infos.len() as u32;
        for (i, schedule_info) in value.schedule_infos.into_iter().enumerate() {
            if i < MAX_SCHEDULE_NUM_FFI {
                schedule_infos.data[i] = schedule_info.try_into()?;
            }
        }

        Ok(BeginAuthenticationOutputFfi { schedule_infos })
    }
}

pub trait BeginAuthenticationCommandExecutor {
    fn execute(
        &self,
        input: BeginAuthenticationInput,
    ) -> Result<BeginAuthenticationOutput, ErrorCode>;
}

pub struct DefaultBeginAuthenticationCommandExecutor;

impl BeginAuthenticationCommandExecutor for DefaultBeginAuthenticationCommandExecutor {
    fn execute(
        &self,
        _input: BeginAuthenticationInput,
    ) -> Result<BeginAuthenticationOutput, ErrorCode> {
        Ok(BeginAuthenticationOutput {
            schedule_infos: Vec::new(),
        })
    }
}

impl_command_handler!(
    BeginAuthenticationCommandHandler,
    BeginAuthenticationInputFfi,
    BeginAuthenticationInput,
    BeginAuthenticationOutputFfi,
    Box<dyn BeginAuthenticationCommandExecutor>
);

// CancelAuthentication - implementation
pub type CancelAuthenticationInput = CancelAuthenticationInputFfi;
pub type CancelAuthenticationOutput = CancelAuthenticationOutputFfi;

pub trait CancelAuthenticationCommandExecutor {
    fn execute(
        &self,
        input: CancelAuthenticationInput,
    ) -> Result<CancelAuthenticationOutput, ErrorCode>;
}

pub struct DefaultCancelAuthenticationCommandExecutor;

impl CancelAuthenticationCommandExecutor for DefaultCancelAuthenticationCommandExecutor {
    fn execute(
        &self,
        _input: CancelAuthenticationInput,
    ) -> Result<CancelAuthenticationOutput, ErrorCode> {
        Err(ErrorCode::GeneralError)
    }
}

impl_command_handler!(
    CancelAuthenticationCommandHandler,
    CancelAuthenticationInputFfi,
    CancelAuthenticationInput,
    CancelAuthenticationOutputFfi,
    Box<dyn CancelAuthenticationCommandExecutor>
);

// UpdateAuthenticationResult - implementation
#[derive(Clone)]
pub struct UpdateAuthenticationResultInput {
    pub context_id: u64,
    pub schedule_result: Vec<u8>,
}

impl TryFrom<UpdateAuthenticationResultInputFfi> for UpdateAuthenticationResultInput {
    type Error = ErrorCode;

    fn try_from(
        value: UpdateAuthenticationResultInputFfi,
    ) -> core::result::Result<Self, ErrorCode> {
        Ok(UpdateAuthenticationResultInput {
            context_id: value.context_id,
            schedule_result: value.schedule_result.try_into()?,
        })
    }
}

#[derive(Clone, Default)]
pub struct UpdateAuthenticationResultOutput {
    pub info: AuthResultInfo,
    pub enrolled_state: EnrolledState,
}

impl TryFrom<UpdateAuthenticationResultOutput> for UpdateAuthenticationResultOutputFfi {
    type Error = ErrorCode;

    fn try_from(value: UpdateAuthenticationResultOutput) -> core::result::Result<Self, ErrorCode> {
        Ok(UpdateAuthenticationResultOutputFfi {
            info: value.info.try_into()?,
            enrolled_state: value.enrolled_state,
        })
    }
}

pub trait UpdateAuthenticationResultCommandExecutor {
    fn execute(
        &self,
        input: UpdateAuthenticationResultInput,
    ) -> Result<UpdateAuthenticationResultOutput, ErrorCode>;
}

pub struct DefaultUpdateAuthenticationResultCommandExecutor;

impl UpdateAuthenticationResultCommandExecutor
    for DefaultUpdateAuthenticationResultCommandExecutor
{
    fn execute(
        &self,
        _input: UpdateAuthenticationResultInput,
    ) -> Result<UpdateAuthenticationResultOutput, ErrorCode> {
        Err(ErrorCode::GeneralError)
    }
}

impl_command_handler!(
    UpdateAuthenticationResultCommandHandler,
    UpdateAuthenticationResultInputFfi,
    UpdateAuthenticationResultInput,
    UpdateAuthenticationResultOutputFfi,
    Box<dyn UpdateAuthenticationResultCommandExecutor>
);

// CheckReuseUnlockResult - implementation

#[derive(Clone)]
pub struct ReuseUnlockParam {
    pub base_param: AuthParamBase,
    pub auth_types: Vec<AuthType>,
    pub reuse_unlock_result_duration: u64,
    pub reuse_unlock_result_mode: ReuseMode,
}

impl TryFrom<ReuseUnlockParamFfi> for ReuseUnlockParam {
    type Error = ErrorCode;

    fn try_from(ffi: ReuseUnlockParamFfi) -> Result<Self, ErrorCode> {
        let mut auth_types = Vec::new();
        for i in 0..ffi.auth_types.len as usize {
            auth_types.push(AuthType::try_from(ffi.auth_types.data[i])?);
        }

        Ok(ReuseUnlockParam {
            base_param: ffi.base_param.try_into()?,
            auth_types,
            reuse_unlock_result_duration: ffi.reuse_unlock_result_duration,
            reuse_unlock_result_mode: ReuseMode::try_from(ffi.reuse_unlock_result_mode)?,
        })
    }
}

#[derive(Clone)]
pub struct CheckReuseUnlockResultInput {
    pub reuse_param: ReuseUnlockParam,
}

impl TryFrom<CheckReuseUnlockResultInputFfi> for CheckReuseUnlockResultInput {
    type Error = ErrorCode;

    fn try_from(ffi: CheckReuseUnlockResultInputFfi) -> Result<Self, ErrorCode> {
        Ok(CheckReuseUnlockResultInput {
            reuse_param: ffi.reuse_param.try_into()?,
        })
    }
}

pub type CheckReuseUnlockResultOutput = CheckReuseUnlockResultOutputFfi;

pub trait CheckReuseUnlockResultCommandExecutor {
    fn execute(
        &self,
        input: CheckReuseUnlockResultInput,
    ) -> Result<CheckReuseUnlockResultOutput, ErrorCode>;
}

pub struct DefaultCheckReuseUnlockResultCommandExecutor;

impl CheckReuseUnlockResultCommandExecutor for DefaultCheckReuseUnlockResultCommandExecutor {
    fn execute(
        &self,
        input: CheckReuseUnlockResultInput,
    ) -> Result<CheckReuseUnlockResultOutput, ErrorCode> {
        let (reuse_token, auth_type, enroll_state) = AuthResultReuseRegistry::get_mut().check_reuse_auth_result(&input.reuse_param)?;
        if reuse_token.len() > MAX_AUTH_TOKEN_LEN_FFI {
            log_e!("bad auth token len:{}", reuse_token.len() as u32);
            return Err(ErrorCode::GeneralError);
        }

        let mut reuse_token_temp = [0; MAX_AUTH_TOKEN_LEN_FFI];
        reuse_token_temp.copy_from_slice(&reuse_token);
        Ok(CheckReuseUnlockResultOutput {
            reuse_info: ReuseUnlockInfoFfi {
                auth_type: auth_type as i32,
                token: AuthTokenArrayFfi {
                    data: reuse_token_temp,
                    len: reuse_token.len() as u32,
                },
                enrolled_state: enroll_state,
            },
        })
    }
}

impl_command_handler!(
    CheckReuseUnlockResultCommandHandler,
    CheckReuseUnlockResultInputFfi,
    CheckReuseUnlockResultInput,
    CheckReuseUnlockResultOutputFfi,
    Box<dyn CheckReuseUnlockResultCommandExecutor>
);

// GetEnrolledState - implementation
pub struct GetEnrolledStateInput {
    pub user_id: i32,
    pub auth_type: AuthType,
}

impl TryFrom<GetEnrolledStateInputFfi> for GetEnrolledStateInput {
    type Error = ErrorCode;

    fn try_from(value: GetEnrolledStateInputFfi) -> core::result::Result<Self, ErrorCode> {
        Ok(GetEnrolledStateInput {
            user_id: value.user_id,
            auth_type: AuthType::try_from(value.auth_type)?,
        })
    }
}

pub type GetEnrolledStateOutput = GetEnrolledStateOutputFfi;

pub trait GetEnrolledStateCommandExecutor {
    fn execute(&self, input: GetEnrolledStateInput) -> Result<GetEnrolledStateOutput, ErrorCode>;
}

pub struct DefaultGetEnrolledStateCommandExecutor;

impl GetEnrolledStateCommandExecutor for DefaultGetEnrolledStateCommandExecutor {
    fn execute(&self, _input: GetEnrolledStateInput) -> Result<GetEnrolledStateOutput, ErrorCode> {
        let enable_status = ConfigManagerRegistry::get().get_enable_status(_input.auth_type, _input.user_id)?;
        if !enable_status {
            log_e!("authType:{} is not support", _input.auth_type as i32);
            return Err(ErrorCode::TypeNotSupport);
        }
        let credential_filter: CredentialFilter = Box::new(move |cred: &Credential| {
            cred.user_id == _input.user_id && cred.auth_type == _input.auth_type
        });
        let mut output: GetEnrolledStateOutput = GetEnrolledStateOutput::default();
        let cred_infos = match UserDbManagerRegistry::get().get_credential_list(credential_filter) {
            Ok(value) => value,
            Err(err) => return Err(err),
        };

        output.enrolled_state.credential_count = cred_infos.len() as u16;
        output.enrolled_state.credential_digest = UserDbManagerRegistry::get().get_enrolled_id(_input.user_id, _input.auth_type)?;
        Ok(output)
    }
}

impl_command_handler!(
    GetEnrolledStateCommandHandler,
    GetEnrolledStateInputFfi,
    GetEnrolledStateInput,
    GetEnrolledStateOutputFfi,
    Box<dyn GetEnrolledStateCommandExecutor>
);
